package com.core.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * @author gang
 */
@Slf4j
public class HttpUtil {

    private final HttpClient httpClient;

    public HttpUtil(HttpClient httpClient) {
        this.httpClient = httpClient;
    }


    public <R> R delete(String url, Map<String, String> headers, Class<R> responseType) {
        HttpDelete request = new HttpDelete(url);
        request.addHeader(new BasicHeader(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.toString()));
        if (headers != null) {
            headers.forEach((k, v) -> request.addHeader(new BasicHeader(k, v)));
        }
        try {
            return execute(responseType, p -> p, request);
        } catch (Exception e) {
            log.error("执行失败", e);
            return null;
        } finally {
            request.releaseConnection();
        }
    }

    public <R> R get(String url, Class<R> clazz) {
        return get(url, clazz, p -> p);
    }

    public <R> R get(String url, TypeReference<R> typeReference) {
        return get(url, String.class, p -> JSON.parseObject(p, typeReference));
    }

    public <T, R> R post(String url, T request, Map<String, String> headers, Class<R> responseType) {
        return post(url, request, responseType, p -> p, null, headers);
    }

    public <T, R> R post(String url, T request, Class<R> responseType) {
        return post(url, request, responseType, p -> p, null, null);
    }

    /**
     * 自定义超时时间的请求
     *
     * @param url            请求地址
     * @param request        请求参数
     * @param responseType   返回结果类型
     * @param connectTimeout 建立连接超时时间
     * @param socketTimeout  读取数据超时时间
     * @return 查询结果
     */
    public <T, R> R post(String url, T request, Class<R> responseType, int connectTimeout, int socketTimeout) {
        RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(10000).setConnectTimeout(connectTimeout)
                .setSocketTimeout(socketTimeout).build();
        return post(url, request, responseType, p -> p, config, null);
    }

    public <T, R> R post(String url, T request, TypeReference<R> typeReference) {
        return post(url, request, String.class, p -> JSON.parseObject(p, typeReference), null, null);
    }

    public <T, R> List<R> postArray(String url, T request, Class<R> listType) {
        return post(url, request, String.class, p -> JSON.parseArray(p, listType), null, null);
    }

    public <T, R, U> R post(String url, T request, Class<U> clazz, Function<U, R> function, RequestConfig requestConfig, Map<String, String> headers) {

        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader(new BasicHeader(HttpHeaders.CONTENT_ENCODING, StandardCharsets.UTF_8.toString()));
        httpPost.addHeader(new BasicHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE));
        if (headers != null) {
            headers.forEach((k, v) -> httpPost.addHeader(new BasicHeader(k, v)));
        }
        String requestString = JSON.toJSONString(request);
        StringEntity requestEntity = new StringEntity(requestString, StandardCharsets.UTF_8);
        httpPost.setEntity(requestEntity);
        if (requestConfig != null) {
            httpPost.setConfig(requestConfig);
        }
        try {
            return execute(clazz, function, httpPost);
        } catch (Exception e) {
            String json = JSON.toJSONString(request);
            if (json.length() > 2000) {
                json = json.substring(0, 2000);
            }
            log.error("http调用过程出现异常,url:[{}],request:[{}]", url, json, e);
            return null;
        } finally {
            httpPost.releaseConnection();
        }
    }

    private <R, U> R execute(Class<U> clazz, Function<U, R> function, HttpRequestBase request) throws Exception {
        String json = httpClient.execute(request, new StringResponseHandler());
        U u = JSON.parseObject(json, clazz);
        return function.apply(u);
    }

    public <U, R> R get(String url, Class<U> clazz, Function<U, R> function) {

        HttpGet httpGet = new HttpGet(url);
        try {
            return execute(clazz, function, httpGet);
        } catch (Exception e) {
            log.error("调用发生异常,url:{}", url, e);
            return null;
        } finally {
            httpGet.releaseConnection();
        }
    }

    private static class StringResponseHandler implements ResponseHandler<String> {

        @Override
        public String handleResponse(HttpResponse response) throws IOException {
            StatusLine statusLine = response.getStatusLine();
            int code = statusLine.getStatusCode();
            if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
                throw new IOException("http调用发生错误,statusCode=" + code);
            }
            HttpEntity entity = response.getEntity();
            InputStream inputStream = entity.getContent();
            try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) > -1) {
                    outputStream.write(buffer, 0, length);
                }
                return outputStream.toString(StandardCharsets.UTF_8.name());
            } catch (IOException e) {
                throw new IOException("关闭错误", e);
            }

        }
    }
}
